package com.sh.d1.gameserver;

import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingFactory;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.sh.d1.gameserver.cmdhandler.CmdHandlerFactory;
import com.sh.d1.gameserver.mq.MqProducer;
import com.sh.util.LoadStatistic;
import com.sh.util.NacosConfigUtil;
import com.sh.util.RedisUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import org.apache.commons.cli.CommandLine;
import org.apache.log4j.PropertyConfigurator;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Properties;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import static com.sh.util.CommandLineUtil.createCmdLn;


/**
 * 游戏服务器入口
 */
public class GameServer {
    /**
     * 日志对象
     */
    static private final Logger LOGGER = LoggerFactory.getLogger(GameServer.class);

    /**
     * 主函数
     *
     * @param argvArray 命令行参数
     */
    static public void main(String[] argvArray) {
        // 设置 log4j 属性文件
        PropertyConfigurator.configure(GameServer.class.getClassLoader().getResourceAsStream("log4j.properties"));

        CommandLine cmdLn=createCmdLn(argvArray);
        if(null==cmdLn){
            System.exit(-1);
            return;
        }


        NacosConfigUtil.init(cmdLn.getOptionValue("nacos_server_addr"),
                "DEFAULT_GROUP", "application.properties",5000);
        boolean success = NacosConfigUtil.loadAllConfigFromNacos();
        if (!success) {
            System.exit(-1);
            return;
        }

        try {
            registerCurrServer(cmdLn);
        } catch (NacosException ex) {
            LOGGER.error(ex.getMessage(),ex);
        }



        GameMsgRecognizer.init();   // 初始化消息识别器
        CmdHandlerFactory.init();   // 初始化工厂
        MySqlSessionFactory.init(); // 初始化 MySql 会话工厂
        RedisUtil.init();           // 初始化 Redis
        MqProducer.init();          // 初始化消息队列

        RedisSusbcriber.startSubcriber();

        EventLoopGroup bossGroup = new NioEventLoopGroup();   // 拉客的, 故事中的美女
        EventLoopGroup workerGroup = new NioEventLoopGroup(); // 干活的, 故事中的男服务生

        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup);
            b.channel(NioServerSocketChannel.class); // 非阻塞的信道, 拉客不停歇!
            b.childHandler(new ChannelInitializer<SocketChannel>() { // 告诉干活的这活应该怎么干?
                @Override
                public void initChannel(SocketChannel ch) {
                    // 在这里告诉干活的这活应该怎么干?
                    ch.pipeline().addLast(
                        new HttpServerCodec(), // 添加 Http 编解码器
                        new HttpObjectAggregator(65535), // 内容不能太长
                        new WebSocketServerProtocolHandler("/websocket"), // WebSocket 协议
                        new GameMsgEncoder(), // 增加游戏消息编码器
                        new GameMsgDecoder(), // 增加游戏消息解码器
                        new GameMsgHandler()  // 最后在这里处理游戏消息
                    );
                }
            });
            b.option(ChannelOption.SO_BACKLOG, 128);
            b.childOption(ChannelOption.SO_KEEPALIVE, true);

            // 绑定服务器端口号
            ChannelFuture f = b.bind(cmdLn.getOptionValue("bind_host"),
                    Integer.parseInt(cmdLn.getOptionValue("bind_port"))).sync();

            if (f.isSuccess()) {
                LOGGER.info(">>> 游戏服务器启动成功! <<<");
            }

            f.channel().closeFuture().sync();
        } catch (Exception ex) {
            // 记录错误日志
            LOGGER.error(ex.getMessage(), ex);
        } finally {
            // 关闭服务器, 大家都歇了吧
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }



    /**
     * 向nacos注册服务
     */
    static private  void registerCurrServer(CommandLine cmdLn) throws NacosException {
        if(null==cmdLn){
            return;
        }
        String  serverAddr= cmdLn.getOptionValue("nacos_server_addr");
        Properties prop=new Properties();

        prop.put("serverAddr",serverAddr);

        NamingService ns= NamingFactory.createNamingService(prop);

        Instance gameServerInstance=new Instance();

        gameServerInstance.setInstanceId(cmdLn.getOptionValue("server_id"));
        gameServerInstance.setIp(cmdLn.getOptionValue("bind_host"));
        gameServerInstance.setPort(Integer.parseInt(cmdLn.getOptionValue("bind_port")));


        Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(()->{
            try {
                int loadNumber = Integer.parseInt(NacosConfigUtil.getValueByKey("LOAD_NUMBER"));
                gameServerInstance.setWeight(loadNumber - LoadStatistic.get());

                ns.registerInstance("com.sh.d1.gameserver",
                        cmdLn.getOptionValue("job_type"),
                        gameServerInstance);
            } catch (NacosException ex) {
                LOGGER.error(ex.getMessage(),ex);
            }
        },0,5, TimeUnit.SECONDS);


    }
}
